package roborally.model;
import be.kuleuven.cs.som.annotate.*;
import roborally.model.interfaces.*;

/**
 * An abstract class representing an element in RoboRally
 * @note 	Every element can be placed on the board, or exist without being positioned on one
 * @Implements	Terminatable
 */
public abstract class Element implements Terminatable{
	
	@Model
	protected Element(){
		myField = null;
	}
		
	/**
	 * Returns the field of an Element object
	 * @return myField
	 */
	@Basic
	public Field getField(){ return myField; }


	/**
	 * Places the element on a field
	 * 
	 * @param field
	 * @effect new.myField = field
	 * @throws IllegalStateException
	 * 		   Thrown when the item already has a field different from null
	 * @throws IllegalArgumentException
	 * 		   Thrown when the field on which the element is to be placed is null
	 */
	public void placeOnBoard(Field field)throws IllegalStateException, IllegalArgumentException { 
		if(getField() != null) throw new IllegalStateException("The element is already placed on another field.");
		if(field == null) throw new IllegalArgumentException("Argument 'field' == null");
		myField = field; 
	}
	/**
	 * Removes an Element object from a board
	 * 
	 * @return result == isPlacedOnBoard()
	 * @post new.myField = null
	 */
	public boolean removeFromBoard(){ 
		if(!isPlacedOnBoard()) return false; //return false if the element was not on a board anyway.
		myField = null; 
		return true;
	}
	/**
	 * Returns whether or not an Item is placed on a board
	 * @return result == (getField()!=null)
	 */
	public boolean isPlacedOnBoard(){ return getField() != null; }
	
	/**
	 * A Field object indicating the field with which the element is linked 
	 */
	private Field myField;
	
	//TERMINATE an element
	
	/**
	 * Returns whether or not the element is terminated
	 * @return isTerminated
	 */
	@Basic @Raw
	public boolean isTerminated(){ return isTerminated; }
	/**
	 * A variable that indicates whether or not the element is terminated
	 */
	protected boolean isTerminated;
	
	/**
	 * Terminate this element.
	 * @note	can be overridden if needed
	 * @post	|new.isTerminated() == true
	 * @effect 	|if(isPlacedOnBoard) then getField().removeElement(this)
	 */
	public void terminate(){
		if(isTerminated) return;
		if(isPlacedOnBoard()) getField().removeElement(this); //remove me from my field before terminating
		this.isTerminated = true;
	}
	
	//declare other methods
	/**
	 * Returns a boolean indicating whether or not this element can be placed on the same field as the given element.
	 */
	public abstract boolean canHaveSameFieldAs(Element otherElement);
	/**
	 * Gives the effect an element experiences when it is hit by the laser of a robot
	 */
	public abstract void hit();
	
	//override object methods
	/**
	 * @return	A string indicating the field where the element is placed on, and if it is terminated or not. 
	 * 			If the element has no field, "not on board" is returned
	 */
	@Override
	public String toString(){
		String sfield;
		if(isPlacedOnBoard()) sfield = getField().toString();
		else sfield = "not on board";
		return "Element on field: " + sfield + "| terminated:" + isTerminated();
	}
}